/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

const { values, reject } = require("lodash")

// 1 promise 有3种状态 pending  fulfilled rejected, pending 可以变为 fulfilled 或者 rejected，改变后，后续也不会改变该状态
// 2 改为 fulfilled 用 resolve 方法实现
// 3 改为 rejected 用 reject 方法实现
// 4 promise 参数是一个函数，需要立即执行
// 5 后会调用resolve或reject方法来改变状态,resolve或reject 是通过参数传入的
// 6 then方法回调函数有2个，一个成功回调，一个失败回调，同时在回调函数中可以获取值
// 7 所以我们需要定义2个变量，一个成功的传值（value），一个失败的传值（reason）分别在调用resolve或reject进行设置
// 8 我们在then方法中调用成功失败回调时，将值直接传入即可在回调函数中获取
// 9 我们在定义then方法时，需要通过_status状态来判断调用成功回调还是失败回调，
// 10 promise 回调函数中异步调用resolve或reject,没有返回结果，所以我们需要处理异步问题
// 11 当我们多次调用then方法时，只返回了最后一个，我们如何处理尼？？需要将每次调用的函数缓存下来，等待执行时依次调用
// 12 then 链式调用，需要then返回一个promise对象
// 13 需判断x 值是否是promise对象，如果是，则需获取其中的值进行返回；故不是则可直接返回

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
  _status = PENDING
  _value = undefined
  _reason = undefined
  _successCallback = []
  _failCallback = []
  constructor (executor) {
    executor(this.resolve, this.reject)
  }
  resolve = (value) => {
    // status 只有在PENDING的情况下才允许改变
    if (this._status !== PENDING) return
    this._status = FULFILLED
    this._value = value
    // this._successCallback && this._successCallback(this._value)
    while (this._successCallback.length) this._successCallback.shift()(this._value)
  }
  reject = (reason) => {
    // status改变后将无法再进行改变
    if (this._status !== PENDING) return
    this._status = REJECTED
    this._reason = reason
    // this._failCallback && this._failCallback(this._reason)
    while (this._failCallback.length) this._failCallback.shift()(this._reason)
  }
  then (successCallback, failCallback) {
    let promise2 = new MyPromise((resolve, reject) => {
      // console.log('this._status', this._status)
      if (this._status === FULFILLED) {
        let x = successCallback && successCallback(this._value)
        // console.log(1111, x)
        // 需判断x 值是否是promise对象，如果是，则需获取其中的值进行返回；故不是则可直接返回

        resolvePromise(x, resolve, reject)
      } else if (this._status === REJECTED) {
        // this._failCallback.push(failCallback)
        let x = failCallback && failCallback(this._reason)
        // console.log(2, x)
        reject(x)
      } else {
        this._successCallback.push(() => {
          let x = successCallback && successCallback(this._value)
          resolvePromise(x, resolve, reject)
        })
        this._failCallback.push(() => {
          let x = failCallback && failCallback(this._reason)
          // console.log(4, x)
          reject(x)
        })
      }
    })
    return promise2
  }
  static all (array) {
    // ['a','b', p2, 'c']
    let result = []
    let index = 0
    return new MyPromise((resolve, reject) => {
      function addResult (key, value) {
        result[key] = value;
        index++
        if (index === array.length) resolve(result)
      }
      for (let i = 0; i < array.length; i++) {
        const curr = array[i]
        if (curr instanceof MyPromise) {
          curr.then(value => addResult(i, value), reason => reject(reason))
        } else {
          addResult(i, array[i])
        }
      }
    })
  }
  static race (array) {
    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < array.length; i++) {
        const curr = array[i]
        if (curr instanceof MyPromise) {
          curr.then(value => resolve(i, value), reason => reject(reason))
        } else {
          resolve(array[i])
        }
      }
    })
  }
  static resolve (value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value));
  }
  catch (failCallback) {
    return this.then(undefined, failCallback)
  }
  finally (callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value);
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }
}

// function resolvePromise (x, resolve, reject) {
//   // console.log('--resolvePromise--',x instanceof MyPromise)
//   if (x instanceof MyPromise) {
//     x.then(resolve, reject)
//   } else {
//     resolve(x)
//   }
// }
// function promise2() {
//   return new MyPromise((resolve, reject) => {
//     resolve('promise2')
//   })
// }
// const promise = new MyPromise((resolve, reject) => {
//   setTimeout(() => {
//     // resolve(1)
//   reject('fail')
//   }, 1000)
// })
// 多次重复调用
// promise.then((value) => {
//   console.log(1, value)
// }, (reason) => {
//   console.log(2, reason)
// })
// promise.then((value) => {
//   console.log(3, value)
// }, (reason) => {
//   console.log(4, reason)
// })
// 链式调用
// const p2 = promise.then((value) => {
//   console.log(1, value)
//   return 2
// }).then((value) => {
//   console.log(3, value)
// }).finally((r) => {
//   console.log('r', r)
// })
// console.log('--promise--', promise)

// MyPromise.race([promise, 'a', 'b', 'c']).then(val=> console.log(val), r => console.log(r))